]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
NotificationDaemon: add support for resident notifications
authorMarina Zhurakhinskaya <marinaz@redhat.com>
Thu, 16 Dec 2010 20:49:47 +0000 (15:49 -0500)
committerMarina Zhurakhinskaya <marinaz@redhat.com>
Thu, 16 Dec 2010 20:49:47 +0000 (15:49 -0500)
Resident notifications don't get removed when they are clicked or
one of their actions is invoked, and are only removed when the app
that created them requests them to be removed or sends another
notification.

Remove the source when a notification associated with it is removed.
Except if the source is a tray icon.

Make sure that we pop down the tray when a notification is clicked
or one of the actions of a non-resident notification is selected.

Based on the initial patch by Jonathan Matthew.

https://bugzilla.gnome.org/show_bug.cgi?id=633412

js/ui/messageTray.js
js/ui/notificationDaemon.js

index d2c10e66b3808d7e1841aa3d0715c5f680a75a29..50a2f5f3b6e5413d3042e0f9c0a25784a3e8932c 100644 (file)
@@ -226,6 +226,7 @@ Notification.prototype = {
     _init: function(source, title, banner, params) {
         this.source = source;
         this.urgent = false;
+        this.resident = false;
         this.expanded = false;
         this._useActionIcons = false;
         this._customContent = false;
@@ -257,7 +258,7 @@ Notification.prototype = {
             function (actor, event) {
                 if (!this._actionArea ||
                     !this._actionArea.contains(event.get_source()))
-                    this.emit('clicked');
+                    this._onClicked();
             }));
 
         // The first line should have the title, followed by the
@@ -484,7 +485,7 @@ Notification.prototype = {
         }
 
         this._buttonBox.add(button);
-        button.connect('clicked', Lang.bind(this, function() { this.emit('action-invoked', id); }));
+        button.connect('clicked', Lang.bind(this, this._onActionInvoked, id));
         this._updated();
     },
 
@@ -492,6 +493,10 @@ Notification.prototype = {
         this.urgent = urgent;
     },
 
+    setResident: function(resident) {
+        this.resident = resident;
+    },
+
     setUseActionIcons: function(useIcons) {
         this._useActionIcons = useIcons;
     },
@@ -696,6 +701,28 @@ Notification.prototype = {
         return false;
     },
 
+    _onActionInvoked: function(actor, id) {
+        this.emit('action-invoked', id);
+        if (!this.resident) {
+            // We don't hide a resident notification when the user invokes one of its actions,
+            // because it is common for such notifications to update themselves with new
+            // information based on the action. We'd like to display the updated information
+            // in place, rather than pop-up a new notification.
+            this.emit('done-displaying');
+            this.destroy();
+        }
+    },
+
+    _onClicked: function() {
+        this.emit('clicked');
+        // We hide all types of notifications once the user clicks on them because the common
+        // outcome of clicking should be the relevant window being brought forward and the user's
+        // attention switching to the window.
+        this.emit('done-displaying');
+        if (!this.resident)
+            this.destroy();
+    },
+
     _onKeyPress: function(actor, event) {
         let symbol = event.get_key_symbol();
         if (symbol == Clutter.Escape) {
@@ -811,6 +838,7 @@ Source.prototype = {
                     this.notification = null;
                     this._notificationDestroyedId = 0;
                     this._notificationClickedId = 0;
+                    this._notificationRemoved();
                 }
             }));
 
@@ -830,8 +858,13 @@ Source.prototype = {
         this._iconBin.child = icon;
     },
 
-    // Default implementation is to do nothing, but subclass can override
+    // Default implementation is to do nothing, but subclasses can override
     _notificationClicked: function(notification) {
+    },
+
+    // Default implementation is to destroy this source, but subclasses can override
+    _notificationRemoved: function() {
+        this.destroy();
     }
 };
 Signals.addSignalMethods(Source.prototype);
@@ -905,6 +938,7 @@ MessageTray.prototype = {
         this._notificationBin.hide();
         this._notificationQueue = [];
         this._notification = null;
+        this._notificationClickedId = 0;
 
         this._summaryBin = new St.Bin({ anchor_gravity: Clutter.Gravity.NORTH_EAST });
         this.actor.add_actor(this._summaryBin);
@@ -926,6 +960,7 @@ MessageTray.prototype = {
         this._summaryNotificationBoxPointer.actor.hide();
 
         this._summaryNotification = null;
+        this._summaryNotificationClickedId = 0;
         this._clickedSummaryItem = null;
         this._clickedSummaryItemAllocationChangedId = 0;
         this._expandedSummaryItem = null;
@@ -1465,6 +1500,8 @@ MessageTray.prototype = {
 
     _showNotification: function() {
         this._notification = this._notificationQueue.shift();
+        this._notificationClickedId = this._notification.connect('done-displaying',
+                                                                 Lang.bind(this, this.escapeTray));
         this._notificationBin.child = this._notification.actor;
 
         this._notificationBin.opacity = 0;
@@ -1562,6 +1599,8 @@ MessageTray.prototype = {
         this._notificationBin.hide();
         this._notificationBin.child = null;
         this._notification.collapseCompleted();
+        this._notification.disconnect(this._notificationClickedId);
+        this._notificationClickedId = 0;
         this._notification = null;
     },
 
@@ -1636,7 +1675,8 @@ MessageTray.prototype = {
 
     _showSummaryNotification: function() {
         this._summaryNotification = this._clickedSummaryItem.source.notification;
-
+        this._summaryNotificationClickedId = this._summaryNotification.connect('done-displaying',
+                                                                               Lang.bind(this, this.escapeTray));
         let index = this._notificationQueue.indexOf(this._summaryNotification);
         if (index != -1)
             this._notificationQueue.splice(index, 1);
@@ -1706,6 +1746,8 @@ MessageTray.prototype = {
         this._summaryNotificationState = State.HIDDEN;
         this._summaryNotificationBoxPointer.bin.child = null;
         this._summaryNotification.collapseCompleted();
+        this._summaryNotification.disconnect(this._summaryNotificationClickedId);
+        this._summaryNotificationClickedId = 0;
         let summaryNotification = this._summaryNotification;
         this._summaryNotification = null;
         if (this._reNotifyWithSummaryNotificationAfterHide) {
index 0f2a6c9cdcd17d30addcc784b9a23dd78a32d413..58f6783496be4161bdebbb36974984ce2d820116 100644 (file)
@@ -291,7 +291,10 @@ NotificationDaemon.prototype = {
                 function(n) {
                     delete this._notifications[id];
                 }));
-            notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
+            notification.connect('action-invoked', Lang.bind(this,
+                function(n, actionId) {
+                    this._emitActionInvoked(id, actionId);
+                }));
         } else {
             notification.update(summary, body, { icon: iconActor,
                                                  bannerMarkup: true,
@@ -305,6 +308,7 @@ NotificationDaemon.prototype = {
         }
 
         notification.setUrgent(hints.urgency == Urgency.CRITICAL);
+        notification.setResident(hints.resident == true);
 
         let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
         source.notify(notification, sourceIconActor);
@@ -352,17 +356,13 @@ NotificationDaemon.prototype = {
         for (let id in this._sources) {
             let source = this._sources[id];
             if (source.app == tracker.focus_app) {
-                source.activated();
+                if (source.notification && !source.notification.resident)
+                    source.notification.destroy();
                 return;
             }
         }
     },
 
-    _actionInvoked: function(notification, action, source, id) {
-        source.activated();
-        this._emitActionInvoked(id, action);
-    },
-
     _emitNotificationClosed: function(id, reason) {
         DBus.session.emit_signal('/org/freedesktop/Notifications',
                                  'org.freedesktop.Notifications',
@@ -421,6 +421,9 @@ Source.prototype = {
     },
 
     _setApp: function() {
+        if (this.app)
+            return;
+
         this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
         if (!this.app)
             return;
@@ -440,12 +443,10 @@ Source.prototype = {
     },
 
     _notificationClicked: function(notification) {
-        notification.destroy();
         this.openApp();
-        this.activated();
     },
 
-    activated: function() {
+    _notificationRemoved: function() {
         if (!this._isTrayIcon)
             this.destroy();
     },